/******************************************************************************
 * Copyright (C) 2021 Batonova
 * All rights reserved.
 *****************************************************************************/

#ifndef COM_INCLUDE_PROTOCOLDEF_H_
#define COM_INCLUDE_PROTOCOLDEF_H_

// #include "dra/core/typedefine.h"
#include "serialize/typedefine.h"

namespace dra {
namespace com {

// using namespace deviceware::base;
// using deviceware::base::BOOLEAN;
// using deviceware::base::UINT8;
// using deviceware::base::INT16;
// using deviceware::base::CHAR;
// using deviceware::base::STRING;
// using deviceware::base::VOID;
// using deviceware::base::INT32;

/** @addtogroup communication
 * @brief common interfaces of dra.com
 *
 * @{
 */

#ifdef HAS_SOMEIP
#define PROTO_NAME_SOMEIP "someip"
#endif  //  HAS_SOMEIP
#ifdef HAS_DDS
#define PROTO_NAME_DDS "dds"
#endif  //  HAS_DDS
#define PROTO_NAME_RAWSOCKET "rawsocket"
#define PROTO_NAME_UNIXSOCKET "unixsocket"
#define PROTO_NAME_BINDER "binder"
#define PROTO_NAME_AP "ap"
#define PROTO_NAME_PPS "pps"

/**
 * @brief Type definitation of protocols.
 *
 */
typedef enum _PROTO_TYPE {
    UNDEFINE_PROTO = 0,  ///< default n/a protocol
#ifdef HAS_SOMEIP
    SOMEIP_PROTO = 1,  ///< SOME/IP
#endif                 // HAS_SOMEIP
#ifdef HAS_DDS
    DDS_PROTO = 2,        ///< DDS
#endif                    // HAS_DDS
    RAWSOCKET_PROTO = 3,  ///< raw socket without application layer protocol
    UNIXSOCKET_PROTO = 4,
    BINDER_PROTO = 5,  ///< binder (only avaliable on Android)
    AP_PROTO = 6,      ///< a AP compatible implementation for other APs
    PPS_PROTO = 7,     ///< QNX Persistent Publish/Subscribe service
    MAX
} PROTO_TYPE;

typedef enum _SERIALIZE_TYPE {
    COMMON = 0,
    SOMEIP = 1,
    BINDER = 2,
    RAWSOCKET = 3,
    DDS = 4,
    YAS = 5,
    MULTIPROTO = 99
}SERIALIZE_TYPE;

/**
 * @brief Judges whether the specified protocol type supports cross-domain communication
 * @param[in] type: protocol type.
 * @return bool. true if supports cross-domain else don't.
 *
 */
static constexpr BOOLEAN isProtoCrossDomain(const PROTO_TYPE type) {
    struct {
        PROTO_TYPE protocolType;
        BOOLEAN isCrossDomain;
    } crossDomainTables[PROTO_TYPE::MAX]{
#ifdef HAS_SOMEIP
        {PROTO_TYPE::SOMEIP_PROTO, true},
#endif  // HAS_SOMEIP
        {PROTO_TYPE::RAWSOCKET_PROTO, true}, {PROTO_TYPE::UNIXSOCKET_PROTO, true},
        {PROTO_TYPE::AP_PROTO, true},        {PROTO_TYPE::BINDER_PROTO, true},
#ifdef HAS_DDS
        {PROTO_TYPE::DDS_PROTO, true},
#endif  // HAS_DDS
        {PROTO_TYPE::PPS_PROTO, false}};
    BOOLEAN isCrossDomain = false;
    for (auto e : crossDomainTables) {
        if (type == e.protocolType) {
            isCrossDomain = e.isCrossDomain;
            break;
        }
    }
    return isCrossDomain;
}

enum class OS_TYPE : UINT8 {
    OS_TYPE_UNDEFINED = 0,
    OS_TYPE_ANDROID = 1,
    OS_TYPE_QNX = 2,
    OS_TYPE_LINUX = 3
};

#if defined(__ANDROID__)
#define CURRENT_OS OS_TYPE::OS_TYPE_ANDROID
#elif defined(__QNX__)
#define CURRENT_OS OS_TYPE::OS_TYPE_QNX
#elif defined(__LINUX__)
#define CURRENT_OS OS_TYPE::OS_TYPE_LINUX
#else
#define CURRENT_OS OS_TYPE::OS_TYPE_UNDEFINED
#endif

static constexpr BOOLEAN isProtoSupported(const PROTO_TYPE type) {
    struct {
        PROTO_TYPE protocolType;
        BOOLEAN isSupported;
    } supportedTables[PROTO_TYPE::MAX]{
#ifdef HAS_SOMEIP
        {PROTO_TYPE::SOMEIP_PROTO, true},
#endif  // HAS_SOMEIP
        {PROTO_TYPE::RAWSOCKET_PROTO, true},
        {PROTO_TYPE::UNIXSOCKET_PROTO, true},
        {PROTO_TYPE::AP_PROTO, true},
        {PROTO_TYPE::BINDER_PROTO, true},
#ifdef HAS_DDS
        {PROTO_TYPE::DDS_PROTO, true},
#endif  // HAS_DDS
        {PROTO_TYPE::PPS_PROTO, (CURRENT_OS == OS_TYPE::OS_TYPE_QNX)}};
    BOOLEAN isSupported = false;
    for (auto e : supportedTables) {
        if (type == e.protocolType) {
            isSupported = e.isSupported;
            break;
        }
    }
    return isSupported;
}

/**
 * @brief Converting int value of protocol types to enum value
 * @param[in] iType: protocol type.
 * @return Enumeration values for protocol types
 *
 */
static constexpr PROTO_TYPE protocolTypeEnum(const INT16 iType) {
    PROTO_TYPE type = UNDEFINE_PROTO;
    if (iType == 1) {
#ifdef HAS_SOMEIP
        type = SOMEIP_PROTO;
#else
        type = UNDEFINE_PROTO;
#endif  // HAS_SOMEIP
    } else if (iType == 2) {
#ifdef HAS_DDS
        type = DDS_PROTO;
#else
        type = UNDEFINE_PROTO;
#endif  // HAS_DDS
    } else if (iType == 3) {
        type = RAWSOCKET_PROTO;
    } else if (iType == 4) {
        type = UNIXSOCKET_PROTO;
    } else if (iType == 5) {
        type = BINDER_PROTO;
    } else if (iType == 6) {
        type = AP_PROTO;
    } else if (iType == 7) {
        type = PPS_PROTO;
    } else {
        type = UNDEFINE_PROTO;
    }
    return type;
}

static constexpr PROTO_TYPE protocolTypeEnum(const CHAR *name) {
    PROTO_TYPE type = UNDEFINE_PROTO;
    if (name != nullptr) {
        if (STRING(PROTO_NAME_BINDER) == STRING(name)) {
            type = BINDER_PROTO;
#ifdef HAS_SOMEIP
        } else if (STRING(PROTO_NAME_SOMEIP) == STRING(name)) {
            type = SOMEIP_PROTO;
#endif  // HAS_SOMEIP
#ifdef HAS_DDS
        } else if (STRING(PROTO_NAME_DDS) == STRING(name)) {
            type = DDS_PROTO;
#endif  // HAS_DDS
        } else if (STRING(PROTO_NAME_RAWSOCKET) == STRING(name)) {
            type = RAWSOCKET_PROTO;
        } else if (STRING(PROTO_NAME_UNIXSOCKET) == STRING(name)) {
            type = UNIXSOCKET_PROTO;
        } else if (STRING(PROTO_NAME_AP) == STRING(name)) {
            type = AP_PROTO;
        } else if (STRING(PROTO_NAME_PPS) == STRING(name)) {
            type = PPS_PROTO;
        } else {
            // type is UNDEFINE_PROTO;
        }
    }
    return type;
}

/**
 * @brief Definition of each bit of Request flag
 * @note example: request->setFlag(REQ_FLAG_SYNC & REQ_FLAG_UNRELIABLE);
 * In addition, request->setFlag(REQ_FLAG_SYNC & REQ_FLAG_ONEWAY) is invalid.
 *
 */
typedef enum _REQ_FLAG {
    REQ_FLAG_ASYNC = 0x0000000000000000,       ///< Asynchronization Request (default)
    REQ_FLAG_SYNC = 0x0000000000000001,        ///< Synchronization Request
    REQ_FLAG_ONEWAY = 0x0000000000000002,      ///< Unresponsive Request (asynchronous)
    REQ_FLAG_UNRELIABLE = 0x0000000000000004,  ///< Unreliable Request
} REQ_FLAG;

/**
 * @brief Definition of each bit of Notification flag
 *
 */
typedef enum _NOTI_FLAG {
    NOTI_FLAG_RELIABLE = 0x0000000000000000,    ///< Reliable Notification (default)
    NOTI_FLAG_UNRELIABLE = 0x0000000000000001,  ///< Unreliable Notification
    NOTI_FLAG_FIELD = 0x0000000000000002        ///< event of Field
} NOTI_FLAG;

/**
 * @brief Definition of MethodID range
 *
 */
typedef enum _METHOD_ID {
    FIRST_METHOD_ID = 0x0001,  ///< Minimum method ID
    LAST_METHOD_ID = 0x7F00,   ///< Maximum method ID
    // 0x7F01 ~ 0x7FFF: reserved ID
} METHOD_ID;

/**
 * @brief Definition of EventID range
 *
 */
typedef enum _EVENT_ID {
    FIRST_EVENT_ID = 0x8000,  ///< Minimum event ID
    LAST_EVENT_ID = 0xFF00,   ///< Maximum event ID
    // 0xFF01 ~ 0xFFFF: reserved ID
} EVENT_ID;

/**
 * @brief Definition of each bit of protocol capacity
 *
 */
typedef enum _CAPACBILITY_TYPE {
    CAP_FD = 0x0000000000000001,  ///< support transmission File Descriptor
} CAP_TYPE;

/**
 * @brief The function proto type of protocols creation.
 *
 */
typedef VOID *(*create_protocol_t)(const CHAR *);

/**
 * @brief the plugin entry definitation
 *
 */
typedef struct _DRA_COM_PLUGIN {
    const CHAR *name;                ///< name of plugin
    const CHAR *author;              ///< author of plugin
    INT32 version;                   ///< version of plugin
    create_protocol_t create_proxy;  ///< creating function of proxy
    create_protocol_t create_stub;   ///< creating function of stub
} DRA_COM_PLUGIN;

/** @}*/

}  // namespace com
}  // namespace dra

#endif  // COM_INCLUDE_PROTOCOLDEF_H_
